package com.calou.shoppinglist.utils;
 
import javax.ejb.EJBHome;
import javax.ejb.EJBLocalHome;
import javax.naming.InitialContext;
import javax.rmi.PortableRemoteObject;
import javax.sql.DataSource;

import com.calou.shoppinglist.errors.ServiceLocatorException;

import java.net.URL;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;

/**
 * This class enables Swing client to look for resources (like home, remote interfaces...).
 * It follows the singleton pattern
 */
public class ServiceLocator {

    // ======================================
    // =             Attributes             =
    // ======================================
    
	// logging
	private static String cname = ServiceLocator.class.getName();

    private InitialContext initalContext;
    
    private Hashtable properties;
    
    
    // Remote object cache
    private Map<String, Object> cache;

    // ======================================
    // =             Singleton              =
    // ======================================
    
    private static final ServiceLocator instance = new ServiceLocator();

    public static ServiceLocator getInstance() {
        return instance;
    }

    private ServiceLocator() throws ServiceLocatorException {
        try {
        	//properties = new Hashtable();
        	//properties.put(Context.INITIAL_CONTEXT_FACTORY, "weblogic.jndi.WLInitialContextFactory");
            //properties.put(Context.PROVIDER_URL, "t3://10.1.60.96:9001");
            //initalContext = new InitialContext(properties);
        	
        	initalContext = new InitialContext();
            cache = new HashMap<String, Object>();
        } catch (Exception e) {
            throw new ServiceLocatorException(e);
        }
    }

    // ======================================
    // =           Business methods         =
    // ======================================
    
    /**
     * will get the ejb Local home factory. If this ejb home factory has already been
     * clients need to cast to the type of EJBHome they desire
     *
     * @param jndiName JNDI name of the EJB that we are looking for
     * @return the EJB Home corresponding to the homeName
     * @throws ServiceLocatorException thrown if lookup problems
     */
    public Object getRemoteInterface(String jndiName) throws ServiceLocatorException {
        String mname = "getRemoteInterface";
        Trace.entering(cname, mname, jndiName);
        Object remoteInterface = getRemoteObject(jndiName);
        return remoteInterface;
    }

    /**
     * will get the ejb Local home factory. If this ejb home factory has already been
     * clients need to cast to the type of EJBHome they desire
     *
     * @return the EJB Home corresponding to the homeName
     */
    public EJBLocalHome getLocalHome(String jndiHomeName) throws ServiceLocatorException {
        String methodName = "getLocalHome";
        Trace.entering(cname, methodName, jndiHomeName);

        EJBLocalHome home = (EJBLocalHome) cache.get(jndiHomeName);
        if (home == null) {
            try {
                home = (EJBLocalHome) initalContext.lookup(jndiHomeName);
                cache.put(jndiHomeName, home);
            } catch (Exception e) {
                throw new ServiceLocatorException(e);
            }
        }
        return home;
    }
    
    /**
     * will get the ejb Remote home factory. If this ejb home factory has already been
     * clients need to cast to the type of EJBHome they desire
     *
     * @return the EJB Home corresponding to the homeName
     */
    public EJBHome getHome(String jndiHomeName, Class className) throws ServiceLocatorException {
        String methodName = "getLocalHome";
        Trace.entering(cname, methodName, jndiHomeName);

        EJBHome home = (EJBHome) cache.get(jndiHomeName);
        if (home == null) {
            try {
                Object objref = initalContext.lookup(jndiHomeName);
                Object obj = PortableRemoteObject.narrow(objref, className);
                home = (EJBHome) obj;
                cache.put(jndiHomeName, home);
            } catch (Exception e) {
                throw new ServiceLocatorException(e);
            }
        }
        return home;
    }
    
    /**
     * will get the DataSource Remote home factory. If this ejb home factory has already been
     * clients need to cast to the type of DataSource they desire
     *
     * @return the EJB Home corresponding to the homeName
     */
    public DataSource getDataSource(String jndiName) throws ServiceLocatorException {
        String methodName = "getDataSource";
        Trace.entering(cname, methodName, jndiName);

        DataSource ds = (DataSource) cache.get(jndiName);
        if (ds == null) {
            try {
            	ds = (DataSource)initalContext.lookup(jndiName);
                cache.put(jndiName, ds);
            } catch (Exception e) {
                throw new ServiceLocatorException(e);
            }
        }
        return ds;
    }
    
    /**
     * @return the URL value corresponding
     *         to the env entry name.
     */
    public URL getUrl(String envName) throws ServiceLocatorException {
        try {
            return (URL) initalContext.lookup(envName);
        } catch (Exception e) {
            throw new ServiceLocatorException(e);
        }
    }
    
    // ======================================
    // =            Private methods         =
    // ======================================
    
    private synchronized Object getRemoteObject(String jndiName) throws ServiceLocatorException {
        String methodName = "getObject";
        Trace.entering(cname, methodName, jndiName);

        Object remoteObject = cache.get(jndiName);
        if (remoteObject == null) {
            try {
                remoteObject = initalContext.lookup(jndiName);
                cache.put(jndiName, remoteObject);
            } catch (Exception e) {
                throw new ServiceLocatorException(e);
            }
        }
        return remoteObject;
    }

}
